/**
 * 	 三 (san - A javascript library)
 * 
 *   何谓三？如下古人对三的三种阐述：
 *   道生一，一生二，二生三，三生万物。 《老子·第四十二章》
 *   二与一为三。 《庄子·齐物论》   （注：天为一，地为二，天地相加成三）
 *   三，天地人之道也，从三数。  《许慎·说文解字》
 *
 */


/**
 * @name san
 * @namespace  Base namespace for the san library.  Checks to see san is
 * already defined in the current scope before assigning to prevent
 * clobbering if base.js is loaded more than once.
 */
var san = san||(function(global,undefined){
	
  	var DOM = global.document,
		toString = Object.prototype.toString;

	var LOAD={
		STANDARD:0, //标准加载 (手动加载依赖模块)
		LAZY:1      //延迟加载 (自动加载依赖模块)
	};
	
	var loadQueue = new Object; //加载队列
				
  	var config = {   //默认配置项
		"debug":true, //调试模式
		"host" :location.protocol + '/' + '/' + location.host+ '/',  //默认服务器地址 wei http://www.example.com/
		"suffix" : ".js", //模块文件后缀
		"mode" : LOAD.STANDARD, //loader加载模式： 标准加载 、延迟加载(自动按需加载)
		"charset" : "utf-8" //默认字符集
	};
	
  	var modules = new Object; //模块仓库

	
	//通常推荐用特性探测替代浏览器嗅探。特性探测的好处是能自动适应未来设备和未知设备，
	//比如if(document.addEventListener) 假设 IE9 支持标准事件，则代码不用修改，
	//就自适应了“未来浏览器”。对于未知浏览器也是如此。但是，这并不意味着浏览器嗅探就得彻底抛弃。
	//当代码很明确就是针对已知特定浏览器的，同时并非是某个特性探测可以解决时，
	//用浏览器嗅探反而能带来代码的简洁，同时也也不会有什么后患。总之，一切皆权衡。
	var  ua = navigator.userAgent.toLowerCase(),
		_check = function(r){
        	return r.test(ua);
		},
		
	    /**
         * Opera客户端识别
         * @memberOf san
         * @type String
         */
		isOpera = _check(/opera/),
		
	    /**
         * Opera客户端识别
         * @memberOf san
         * @type String
         */		
		isIE=!isOpera&&_check(/msie/), //Opera可以模仿IE模式	
	    /**
         * Webkit客户端识别
         * @memberOf san
         * @type String
         */			
		isWebkit = _check(/webkit/),
	    /**
         * Chrome客户端识别
         * @memberOf san
         * @type String
         */			
		isChrome = _check(/chrome/),
	    /**
         * Firefox客户端识别
         * @memberOf san
         * @type String
         */			
		isFirefox = _check(/firefox/);
	
	
    
    /**
     * 对象属性复制,默认不覆盖对象已有的属性, 目标对象会被修改
     * @lends san
     * @param {Object} target 目标对象
     * @param {Object} source 源对象
     * @paream {Boolean} override 覆盖模式 {optional}
     * @return {Object} target 对象
     */
    var mix = function(/**{Object}...**/){
		var i=1,
		len=arguments.length,
		target=arguments[0]||{},
		source,
		name,
		override=false;
		
		//最后一个参数为boolean类型即为覆盖模式参数
		if(typeof arguments[len-1] === "boolean"){
				--len;
				override=arguments[len-1];
		}	
		
		for(;i<len;++i){
			source=arguments[i];
			for(name in source){
				if(typeof target[name] === "undefined"){
					target[name]= source[name];
				}else{
					!override||(target[name]= source[name]);				
				}
			}
		}
    
        return target;
    };
	
    /**
     * 对象是否可以迭代 ：原生数组 NodeList HTMLCollection
     * @lends san
     * @param {Object} obj
     */
    var isIterable = function(obj){
        //array or arguments
        if (isArray(obj) || obj.callee) {
            return true;
        }
        //node list type
        if (/NodeList|HTMLCollection/.test(toString.call(obj))) {
            return true;
        }
        //NodeList has an item and length property
        //IXMLDOMNodeList has nextNode method, needs to be checked first.
        return ((typeof obj.nextNode != 'undefined' || obj.item) && isNumber(obj.length));
    };
        
    /**
     * 转化任意对象为数组
     * @lends san
     * @param {Object} obj
     * @param {Number} opt_start
     * @param {Number} opt_end
     * @return (Array) array
     */
    var toArray = function(obj,opt_start,opt_end){
		//the null and ndefined return empty array
	    if (obj === null || obj === undefined) return [];
		
		var len = obj.length;
		
        //convert other object, but the strings and functions also have 'length' property
        if (typeof len !== 'number' || isString(obj) || isFunction(obj)) {
            return [obj];
        }
		
		var start= opt_start || 0, end = opt_end || len;	
		
		// ie 不支持用 slice 转换 NodeList, 降级到普通方法
        if (obj.item && isIE) {
            var ret = [];
            for (var i = 0; i < len; ++i) {
                ret[i] = obj[i];
            }
            return ret.slice(start , end);
        }

        // other array-like object
        return Array.prototype.slice.call(obj, start, end);
	};
	

    /**
     * 数组判断
     * @param {Object} obj
     * @retrun {Boolean}
     */
    var isArray = function(obj){
        //首选浏览器内置提供的isArray方法
        if (Array.isArray) {
            return Array.isArray(obj);
        }
        
        return !!(obj && //是否为真值
        typeof obj === "object" && //是否为 object array 或  null 对象
        typeof obj.length === "number" && //是否有数值类型的length属性
        !obj.propertyIsEnumerable("length")); //length属性是否可以枚举，只有数组中length属性不可枚举
        
        //下面是其他判断数组的方法，jQuery使用了此方法，在chrome下性能上不及目前的方法，
		//测试结果相差将近一倍
        //toString.call(object) == "[object Array]";
		
		//通过instanceof 操作符判断
		//obj instanceof Array
    };
    
    /**
     * 函数判断
     * @param {Object} obj
     * @retrun {Boolean}
     */
    var isFunction = function(obj){
        return typeof obj === "function";
        //toString.call(obj)=="[object Function]"
    };
    
    /**
     * 数字判断
     * @param {Object} obj
     * @return {Boolean} 为无穷大Infinity与负无穷大-InfInity时返回false
     */
    var isNumber = function(obj){
		//当obj通过 new Nubmer 封装类构造时   typeof obj === "object" 
        return (typeof obj === "number"||obj instanceof Number) && isFinite(obj);
    	
		//toString.call(obj)==="object Number"
	};
    /**
     * 字符串判断
     * @param {Object} obj
     * @return {Boolean}
     */
    var isString = function(obj){
		//当obj通过 new String 封装类构造时   typeof obj === "object" 
        return typeof obj === "string"||obj instanceof String;
		
		//toString.call(obj)==="[object String]"
    };
    
    /**
     * 布尔判断
     * @param {Object} obj
     * @return {Boolean}
     */
    var isBoolean = function(obj){
		//当obj通过 new Boolean 封装类构造时   typeof obj === "object" 
        return typeof obj === "boolean"||obj instanceof Boolean;
		
		//toString.call(obj)==="object Boolean"
        
    };
    
    /**
     * 日期判断
     * @param {Object} obj
     * @return {Boolean}
     */
    var isDate = function(obj){
        return toString.call(obj) === "[object Date]";
    };
	
	/**
	 * 对象判断
	 * @param {Object} obj
	 */
	var isObject =function(obj){
		return toString.call(obj) === "[object Object]";
	}
    
    /**
     * 空对象判断
     * @param {Object} obj
     * @retrun {Boolean}
     */
    var isEmptyObject = function(obj){
        for (var name in obj) {
            return false;
        }
        return true;
    };
	
    /**
     * 空判断
     * 
     * @example
     * san.isEmpty(null) === true
     * san.isEmpty(undefined) === true
     * san.isEmpty([]) === true
     * san.isEmpty("")===true
     * san.isEmpty("",false) === true
     * san.isEmpty({})=== true
     * 
     * @param {Mixed} value The value to test
     * @param {Boolean} opt_allowBlank  true to allow empty strings (defaults to false)
     * @return {Boolean}
     */
    var isEmpty = function(obj, opt_allowBlank){
        return obj === null || obj === undefined || ((isArray(obj) && !obj.length)) ||(isObject(obj) && isEmptyObject(obj))|| (!opt_allowBlank ? obj === "" : false);
    };
	
    /**
     * 回掉函数迭代处理
     * @param {Array|NodeList|Mixed} object 
     * @param {Function} callback(value,name) 回掉函数
     * @return {Object} obj 返回传入的对象
     */
    var each = function(obj, callback){
        var name, i = 0, 
		len = obj.length, 
		isObj = len === undefined || isFunction(obj);
        
        if (isObj) {
            for (name in obj) {
                if (callback.call(obj[name], obj[name], name) === false) {
                    break;
                }
            }
        } else { //可迭代对象(原生数组 NodeList HTMLCollection)
            for (var value = obj[0]; i < len && callback.call(value, value, i) !== false; value = obj[++i]) {
            }
        }
            
        return obj;
        
    }
    
    /**
     * 抛出异常
     * @param {String} msg
     * @throw {String} "Error: " + msg
     */
    var error = function(msg){
		if (config.debug)
        	throw new Error(msg);
    };
	
    /**
     * 日志记录
     * @param {...String} msg
     */
    var log = function(){
		
        if (config.debug && global['console'] !== undefined && global['console'].log) {
			var logs=[];
            for (var i = 0; i < arguments.length; ++i) {
				logs.push(arguments[i]);
			}  
			
			global['console'].log(logs.join(' '));
        }   
        return this;
    };
    
    /**
     * 命名空间注册
     * @param {...String} ns 命名空间
     * @param {Object=} opt_global 全局命名空间
     * @return {Object} 返回指定的名称空间，如果该名称空间不存在就创建它。
     * @example 
     *  san.namespace("lang.JSON",san); //等同于San.namespace("san.lang.JSON"); 
     *  san.namespace("lang.JSON","lang.Array",san);
     * 
     */
    var namespace = function(){
		var len = arguments.length,ns=global,i,j,l;
		
		if(isObject(arguments[len-1])){	 //末尾参数类型判断
			ns=arguments[len-1];
			len--;
		}
		
		for (i = 0; i < len; i++) {
			var arr = ("" + arguments[i]).split(".");
			
			for (j = 0, l = arr.length; j < l; j++) {
				ns = ns[arr[j]] = ns[arr[j]] || {};
				
			}
		}
        
        return ns;
        
    };
	
	/**
	 * 模块检查
	 * @private
	 * @param {String} ns
	 */
	function checkModule(ns){
		return loadQueue[ns] || modules.hasOwnProperty(ns);	
	}
			   
	/**
     * 模块声明，模块名称大小写敏感
     * @param {String} ns 命名空间
     * @param {Function} fn 模块
     * @param {String|Array} deps 依赖模块 "fx.Base fx.Color" ["fx.Base","fx.Color"]
     * @param {Object} global 全局命名空间
     * @return {Object} san
     * @example  san.def("lang.JSON",function(san){});
     */
    var def=  function(ns, fn, deps, global) {
		
			var self = this;
			
			var registModule = function(ns, fn, global){
				
				if (!modules.hasOwnProperty(ns)) {
					
					var arr = ns.split("."),module = arr.pop(),parentNs = arr.join(".");						
					//注册模块的上级命名空间，默认全局为self
					var parent= namespace(parentNs, global || self);
					//执行fn，然后加入到模块 和 命名空间中	
					parent[module] = modules[ns] = fn(self);
		
					//log				
					log(ns + " module registed!");
				}
				
			};
		
			//当前模块不存在时才注册模块
			if (!checkModule(ns)) {
				
				/**
				 * 模块注册
				 * @private
				 * @param {String} ns
				 * @param {Function} fn
				 */


				//依赖模块加载检查
				if (deps && config.mode === LOAD.LAZY) { //有依赖模块  且 以延迟模式加载依赖
				
					if (isString(deps)) { //支持String格式描述依赖 ，如："fx.Base fx.Color"
						deps = deps.split(" ");
					}
					
					var len =  deps.length;
					
					for (var i = 0; i < len; ++i) {
						
						var moduleNs=deps[i];
			
						if (checkModule(moduleNs)) {  //模块未注册 且 未加入加载队列
					
							loadQueue[moduleNs] = 1; //加入加载队列中
		
							var modulePath = moduleNs.split(".").join("/");
							
							var url = config.host + modulePath + config.suffix;
							
							load('script', url);
							
							//load加载完成后，但此时加载的js可能还未执行
							//load('script', url, function(){
							//	--willLoad;
							//})();
							
						}						
					}
					
					(function(){
                                
                        var isListenerInit = false; //加载依赖监听器是否已经初始化
                        var intervalNumber, stopwatch;
                        
                        (function(){
                        
                            if (!isListenerInit) { //初始化判断
                                stopwatch = new Date;
                                isListenerInit = true;
                                intervalNumber = setInterval(arguments.callee, 20); //启动加载依赖监听器
                            }
							
							var allRegisted = true;
							
							for(var i = 0; i < len; ++i){
								if(!modules.hasOwnProperty(deps[i])){
									allRegisted = false;
									return;
								}
							}
                            
                            if (allRegisted || (new Date - stopwatch) > 5000) { //依赖模块已加载  或 超时
                            	registModule(ns, fn, global);
                                clearInterval(intervalNumber);
                                return;
                            }
							
                            log(ns, "waiting", moduleNs);
                            
                        })();
							
							
					});
							
					
					
					
				}else {  //无依赖模块  或 以标准模式加载依赖
					registModule(ns, fn, global);
				}

			}else{
				//log				
				log(ns + " have been added before");
			}
			
            return this;
       };	   

	/**
	 * 模块调用,模块名称大小写敏感
	 * @param {String} moduleNs 模块名称
	 * @return {Object} 模块对象
	 */
	var require = function(moduleNs){
				
		//如果模块是单例对象类型模块，则直接返回，如果是构造器方法类型模块，则返回实例化对象
		//return isObject(mod) ? mod : new mod;
		return modules[moduleNs];

	};
	
	/**
	 * 加载资源文件
	 * @param {String} type 资源类型
	 * @param {String} url  资源地址 如：http://www.example.com/js/san/base.js
	 * @param {Function} opt_callback 加载成功后得到回调函数
	 */
	var load = function(type, url, opt_callback){
		
		var head = document.getElementsByTagName("head")[0] || document.body;

		// Handle loading state
		var done = false;
		
		var callbackfn = function() {
				if ( !done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") ) {
					done = true;
					
					if(isFunction(opt_callback)) opt_callback(); //加载成功后的回调函数

				}
		};
		
		switch(type) {
			case 'script':		
				var script = document.createElement("script");
				script.charset = config.charset; //字符集设置
				script.type = 'text/javascript';
            	script.async = true; //异步加载属性设置（HTML5 规范）
				script.src = url; 
				script.onload = script.onreadystatechange = callbackfn;				
			    head.appendChild(script); 

				break;
				
			case 'style':
                var style = document.createElement('link');
                style.rel = "stylesheet";
                style.type = "text/css";
                style.media = "all";
                style.href = url;
				style.onload = style.onreadystatechange = callbackfn;				
                head.appendChild(style);
            
				break;	
							
			default: 
				log(type,"is Unsupported resource type!");
		}

		
	};

   /**
    * 测试输出
    * @param {Object} obj
    */ 
   var dump = function(obj){
   		var msg="Type:"+toString.call(obj)+" "+obj+"\n", name;
		if (isObject(obj)) {
			var hasPM = "Object Property&Method:\n";
			for (name in obj) {
				if (hasPM) {
					msg += hasPM;
					hasPM = false;
				}
				msg += ("  " + name + ":" + obj[name] + "\n");
			}
		}
		log(msg);
		
	};
	
	
	/**
	 * dom查询
	 * @param {String} query
	 * @param {Object} context
	 */	
    var query = function(query, context){
    
        try {
            var NodeList = require("dom.NodeList"), Selector = require("dom.Selector");
			  
            return new NodeList(Selector.select(query, context));
        } 
        catch (e) {
            error("dom.NodeList or dom.Selector do not registed");
        }
    };
    
       return  {
	   	
           "isOpera" : isOpera,
		   
           "isIE" : isIE,
		   
           "isWebkit" : isWebkit,
		   
           "isChrome": isChrome,
		   
           "isFirefox": isFirefox,
		   
		   "isIterable":isIterable,
           
           "isArray": isArray,
           
           "isFunction": isFunction,
           
           "isObject": isObject,
		   
		   "isEmptyObject": isEmptyObject,
           
           "isNumber": isNumber,
           
           "isString": isString,
           
           "isBoolean": isBoolean,
           
           "isDate": isDate,
           
           "isEmpty": isEmpty,
		   
		   "toArray": toArray,
		   
		   "each": each,
		   
		   "mix": mix,
           
           "log": log,
           
           "error": error,
           
           "namespace": namespace,
           
           "def": def,
		   
		   "load": load,
		   
		   "require": require,
		   
		   "dump": dump,
		   
		   "query": query,

		   "config":config
       };
    
    
})(this);

/**
 * NOTES:
 *
 * 2010.10
 *  - def,require 提供对模块modules访问共享方式。
 *  - ready, available 方法决定外部代码的基本调用方式，提供了一个简单的弱沙箱。
 *  - mix 方法，决定了类库代码的基本实现方式，充分利用 mixin 特性和 prototype 方式来实现代码。
 *  - namespace 方法，决定子库的实现和代码的整体组织。
 *  - log, error 方法，简单的调试工具和报错机制。
 *  
 *
 * TODO:
 *  - 
 *
 */










